/*
 *  AS IS License 
 * ==Naive Coders==
 */
package com.naivecoders.refex;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

import com.naivecoders.refex.functions.FCode;

/**
 * aaz=t(a aav(k) bbt(pt pq)
 *
 * @author andrew
 */
public abstract class Tokenizer {

    private int line;
    private int column;
    private boolean inComment;
    private final List<FCode> label;
    private boolean inLabel;
    private final char[] input;

    public Tokenizer() {
        this.input = new char[1024];
        label = new ArrayList<>();
    }

    protected abstract void onProc() throws IllegalSyntaxException;

    protected abstract void onProcEnd() throws IllegalSyntaxException;

    protected abstract void onList() throws IllegalSyntaxException;

    protected void onIList() throws IllegalSyntaxException {
        onList();
        appendLabel(FCode.I.toString().charAt(0));
        onLabel();
    }
    protected abstract void onListEnd() throws IllegalSyntaxException;

    protected abstract void onCall() throws IllegalSyntaxException;

    protected abstract void onCallEnd() throws IllegalSyntaxException;

    protected abstract void onLabel() throws IllegalSyntaxException;

    protected void onLineEnd() {
        line++;
        column = 0;
    }

    public void parce(Reader inputReader) throws IOException,
            IllegalSyntaxException {
        int r;
        try {
            do {
                r = inputReader.read(input);
                for (int i = 0; i < r; i++) {
                    onChar(input[i]);
                    column++;
                }
            } while (r > 0);
        } finally {
            inputReader.close();
        }
    }

    protected void resetLabel() {
        label.clear();
    }

    protected void onChar(char c) throws IllegalSyntaxException {
        boolean iInLabel = false;
        if (inComment) {
            switch (c) {
                case '\n':
                    onLineEnd();
                    inComment = false;
                    break;
            }
        } else {
            switch (c) {
                case '\r':
                    break;
                case '\n':
                    onLineEnd();
                    fireOnLabel();
                    resetLabel();
                    break;
                case '}':
                case ']':
                    fireOnLabel();
                    onListEnd();
                    resetLabel();
                    break;
                case '[':
                    fireOnLabel();
                    onList();
                    resetLabel();
                    break;
                case '{':
                    fireOnLabel();
                    onIList();
                    resetLabel();
                    break;
                case '>':
                    fireOnLabel();
                    onCallEnd();
                    resetLabel();
                    break;
                case '<':
                    fireOnLabel();
                    onCall();
                    resetLabel();
                    break;
                case ' ':
                    fireOnLabel();
                    resetLabel();
                    break;
                case '/':
                    fireOnLabel();

                    inComment = true;
                    break;
                case ';':
                    onProcEnd();
                    break;
                case '=':
                    onProc();
                    resetLabel();
                    break;
                default:
                    appendLabel(c);
                    iInLabel = true;
            }
        }
        inLabel = iInLabel;
    }

    protected FName copyLabel() throws IllegalSyntaxException {
        if (label.size() < 1) {
            throwIllegalSyntax("Unexpected space");
        }
        return new FName(label.toArray(new FCode[label.size()]));
    }

    private void appendLabel(char ch) {
        label.add(FCode.resolve(ch));
    }

    protected void throwIllegalSyntax(Throwable t)
            throws IllegalSyntaxException {
        IllegalSyntaxException ise = new IllegalSyntaxException(line, column,
                t.getMessage());
        ise.initCause(t);
        throw ise;
    }

    protected void throwIllegalSyntax(String why, Object... args)
            throws IllegalSyntaxException {
        throw new IllegalSyntaxException(line, column, String.format(why, args));
    }

    private void fireOnLabel() throws IllegalSyntaxException {
        if (inLabel) {
            onLabel();
        }
    }
}